﻿// This is an open source non-commercial project. Dear PVS-Studio, please check it.
// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com

// ReSharper disable CheckNamespace
// ReSharper disable CommentTypo
// ReSharper disable IdentifierTypo
// ReSharper disable InconsistentNaming
// ReSharper disable UnusedMember.Global

/* SyntaxHighlighter.cs --
 * Ars Magna project, http://arsmagna.ru
 */

#region Using directives

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Globalization;
using System.IO;
using System.Text.RegularExpressions;
using System.Xml;

#endregion

#nullable enable

namespace Fctb;

/// <summary>
/// Умеет раскрашивать текст в соответствии с синтаксисом языка программирования.
/// </summary>
public class SyntaxHighlighter
    : IDisposable
{
    /// <summary>
    ///
    /// </summary>
    //styles
    protected static readonly Platform platformType = PlatformType.GetOperationSystemPlatform();

    /// <summary>
    ///
    /// </summary>
    public readonly Style BlueBoldStyle = new TextStyle (Brushes.Blue, null, FontStyle.Bold);

    /// <summary>
    ///
    /// </summary>
    public readonly Style BlueStyle = new TextStyle (Brushes.Blue, null, FontStyle.Regular);

    /// <summary>
    ///
    /// </summary>
    public readonly Style BoldStyle = new TextStyle (null, null, FontStyle.Bold | FontStyle.Underline);

    /// <summary>
    ///
    /// </summary>
    public readonly Style BrownStyle = new TextStyle (Brushes.Brown, null, FontStyle.Italic);

    /// <summary>
    ///
    /// </summary>
    public readonly Style GrayStyle = new TextStyle (Brushes.Gray, null, FontStyle.Regular);

    /// <summary>
    ///
    /// </summary>
    public readonly Style GreenStyle = new TextStyle (Brushes.Green, null, FontStyle.Italic);

    /// <summary>
    ///
    /// </summary>
    public readonly Style MagentaStyle = new TextStyle (Brushes.Magenta, null, FontStyle.Regular);

    /// <summary>
    ///
    /// </summary>
    public readonly Style MaroonStyle = new TextStyle (Brushes.Maroon, null, FontStyle.Regular);

    /// <summary>
    ///
    /// </summary>
    public readonly Style RedStyle = new TextStyle (Brushes.Red, null, FontStyle.Regular);

    /// <summary>
    ///
    /// </summary>
    public readonly Style BlackStyle = new TextStyle (Brushes.Black, null, FontStyle.Regular);

    /// <summary>
    ///
    /// </summary>
    protected readonly Dictionary<string, SyntaxDescriptor> descByXMLfileNames = new ();

    /// <summary>
    ///
    /// </summary>
    protected readonly List<Style> resilientStyles = new (5);

    #nullable enable

    /// <summary>
    ///
    /// </summary>
    protected Regex CSharpAttributeRegex;

    /// <summary>
    ///
    /// </summary>
    protected Regex CSharpClassNameRegex;

    /// <summary>
    ///
    /// </summary>
    protected Regex CSharpCommentRegex1;

    /// <summary>
    ///
    /// </summary>
    protected Regex CSharpCommentRegex2;

    /// <summary>
    ///
    /// </summary>
    protected Regex CSharpCommentRegex3;

    /// <summary>
    ///
    /// </summary>
    protected Regex CSharpKeywordRegex;

    /// <summary>
    ///
    /// </summary>
    protected Regex CSharpNumberRegex;

    /// <summary>
    ///
    /// </summary>
    protected Regex CSharpStringRegex;

    /// <summary>
    ///
    /// </summary>
    protected Regex HTMLAttrRegex;

    /// <summary>
    ///
    /// </summary>
    protected Regex HTMLAttrValRegex;

    /// <summary>
    ///
    /// </summary>
    protected Regex HTMLCommentRegex1;

    /// <summary>
    ///
    /// </summary>
    protected Regex HTMLCommentRegex2;

    /// <summary>
    ///
    /// </summary>
    protected Regex HTMLEndTagRegex;

    /// <summary>
    ///
    /// </summary>
    protected Regex HTMLEntityRegex;

    /// <summary>
    ///
    /// </summary>
    protected Regex HTMLTagContentRegex;

    /// <summary>
    ///
    /// </summary>
    protected Regex HTMLTagNameRegex;

    /// <summary>
    ///
    /// </summary>
    protected Regex HTMLTagRegex;

    /// <summary>
    ///
    /// </summary>
    protected Regex XMLAttrRegex;

    /// <summary>
    ///
    /// </summary>
    protected Regex XMLAttrValRegex;

    /// <summary>
    ///
    /// </summary>
    protected Regex XMLCommentRegex1;

    /// <summary>
    ///
    /// </summary>
    protected Regex XMLCommentRegex2;

    /// <summary>
    ///
    /// </summary>
    protected Regex XMLEndTagRegex;

    /// <summary>
    ///
    /// </summary>
    protected Regex XMLEntityRegex;

    /// <summary>
    ///
    /// </summary>
    protected Regex XMLTagContentRegex;

    /// <summary>
    ///
    /// </summary>
    protected Regex XMLTagNameRegex;

    /// <summary>
    ///
    /// </summary>
    protected Regex XMLTagRegex;

    /// <summary>
    ///
    /// </summary>
    protected Regex XMLCDataRegex;

    /// <summary>
    ///
    /// </summary>
    protected Regex XMLFoldingRegex;

    /// <summary>
    ///
    /// </summary>
    protected Regex JScriptCommentRegex1;

    /// <summary>
    ///
    /// </summary>
    protected Regex JScriptCommentRegex2;

    /// <summary>
    ///
    /// </summary>
    protected Regex JScriptCommentRegex3;

    /// <summary>
    ///
    /// </summary>
    protected Regex JScriptKeywordRegex;

    /// <summary>
    ///
    /// </summary>
    protected Regex JScriptNumberRegex;

    /// <summary>
    ///
    /// </summary>
    protected Regex JScriptStringRegex;

    /// <summary>
    ///
    /// </summary>
    protected Regex JSONKeywordRegex;

    /// <summary>
    ///
    /// </summary>
    protected Regex JSONNumberRegex;

    /// <summary>
    ///
    /// </summary>
    protected Regex JSONStringRegex;

    /// <summary>
    ///
    /// </summary>
    protected Regex LuaCommentRegex1;

    /// <summary>
    ///
    /// </summary>
    protected Regex LuaCommentRegex2;

    /// <summary>
    ///
    /// </summary>
    protected Regex LuaCommentRegex3;

    /// <summary>
    ///
    /// </summary>
    protected Regex LuaKeywordRegex;

    /// <summary>
    ///
    /// </summary>
    protected Regex LuaNumberRegex;

    /// <summary>
    ///
    /// </summary>
    protected Regex LuaStringRegex;

    /// <summary>
    ///
    /// </summary>
    protected Regex LuaFunctionsRegex;

    /// <summary>
    ///
    /// </summary>
    protected Regex PHPCommentRegex1;

    /// <summary>
    ///
    /// </summary>
    protected Regex PHPCommentRegex2;

    /// <summary>
    ///
    /// </summary>
    protected Regex PHPCommentRegex3;

    /// <summary>
    ///
    /// </summary>
    protected Regex PHPKeywordRegex1;

    /// <summary>
    ///
    /// </summary>
    protected Regex PHPKeywordRegex2;

    /// <summary>
    ///
    /// </summary>
    protected Regex PHPKeywordRegex3;

    /// <summary>
    ///
    /// </summary>
    protected Regex PHPNumberRegex;

    /// <summary>
    ///
    /// </summary>
    protected Regex PHPStringRegex;

    /// <summary>
    ///
    /// </summary>
    protected Regex PHPVarRegex;

    /// <summary>
    ///
    /// </summary>
    protected Regex SQLCommentRegex1;

    /// <summary>
    ///
    /// </summary>
    protected Regex SQLCommentRegex2;

    /// <summary>
    ///
    /// </summary>
    protected Regex SQLCommentRegex3;

    /// <summary>
    ///
    /// </summary>
    protected Regex SQLCommentRegex4;

    /// <summary>
    ///
    /// </summary>
    protected Regex SQLFunctionsRegex;

    /// <summary>
    ///
    /// </summary>
    protected Regex SQLKeywordsRegex;

    /// <summary>
    ///
    /// </summary>
    protected Regex SQLNumberRegex;

    /// <summary>
    ///
    /// </summary>
    protected Regex SQLStatementsRegex;

    /// <summary>
    ///
    /// </summary>
    protected Regex SQLStringRegex;

    /// <summary>
    ///
    /// </summary>
    protected Regex SQLTypesRegex;

    /// <summary>
    ///
    /// </summary>
    protected Regex SQLVarRegex;

    /// <summary>
    ///
    /// </summary>
    protected Regex VBClassNameRegex;

    /// <summary>
    ///
    /// </summary>
    protected Regex VBCommentRegex;

    /// <summary>
    ///
    /// </summary>
    protected Regex VBKeywordRegex;

    /// <summary>
    ///
    /// </summary>
    protected Regex VBNumberRegex;

    /// <summary>
    ///
    /// </summary>
    protected Regex VBStringRegex;

    #nullable restore

    protected SyntaxTextBox currentTb;

    public static RegexOptions RegexCompiledOption =>
        platformType == Platform.X86
            ? RegexOptions.Compiled
            : RegexOptions.None;

    /// <summary>
    /// Конструктор.
    /// </summary>
    public SyntaxHighlighter
        (
            SyntaxTextBox currentTb
        )
    {
        this.currentTb = currentTb;
    }

    #region IDisposable Members

    /// <inheritdoc cref="IDisposable.Dispose"/>
    public void Dispose()
    {
        foreach (var desc in descByXMLfileNames.Values)
        {
            desc.Dispose();
        }
    }

    #endregion

    /// <summary>
    /// Highlights syntax for given language
    /// </summary>
    public virtual void HighlightSyntax
        (
            Language language,
            TextRange range
        )
    {
        switch (language)
        {
            case Language.CSharp:
                CSharpSyntaxHighlight (range);
                break;

            case Language.VB:
                VBSyntaxHighlight (range);
                break;

            case Language.HTML:
                HTMLSyntaxHighlight (range);
                break;

            case Language.XML:
                XMLSyntaxHighlight (range);
                break;

            case Language.SQL:
                SQLSyntaxHighlight (range);
                break;

            case Language.PHP:
                PHPSyntaxHighlight (range);
                break;

            case Language.JS:
                JScriptSyntaxHighlight (range);
                break;

            case Language.Lua:
                LuaSyntaxHighlight (range);
                break;

            case Language.JSON:
                JSONSyntaxHighlight (range);
                break;
        }
    }

    /// <summary>
    /// Highlights syntax for given XML description file
    /// </summary>
    public virtual void HighlightSyntax
        (
            string xmLdescriptionFile,
            TextRange range
        )
    {
        if (!descByXMLfileNames.TryGetValue (xmLdescriptionFile, out var desc))
        {
            var doc = new XmlDocument();
            var file = xmLdescriptionFile;
            if (!File.Exists (file))
            {
                file = Path.Combine (AppDomain.CurrentDomain.BaseDirectory, Path.GetFileName (file));
            }

            doc.LoadXml (File.ReadAllText (file));
            desc = ParseXmlDescription (doc);
            descByXMLfileNames[xmLdescriptionFile] = desc;
        }

        HighlightSyntax (desc, range);
    }

    /// <summary>
    ///
    /// </summary>
    public virtual void AutoIndentNeeded
        (
            object? sender,
            AutoIndentEventArgs eventArgs
        )
    {
        var textBox = (SyntaxTextBox) sender!;
        var language = textBox.Language;
        switch (language)
        {
            case Language.CSharp:
                CSharpAutoIndentNeeded (sender, eventArgs);
                break;

            case Language.VB:
                VBAutoIndentNeeded (sender, eventArgs);
                break;

            case Language.HTML:
                HTMLAutoIndentNeeded (sender, eventArgs);
                break;

            case Language.XML:
                XMLAutoIndentNeeded (sender, eventArgs);
                break;

            case Language.SQL:
                SQLAutoIndentNeeded (sender, eventArgs);
                break;

            case Language.PHP:
                PHPAutoIndentNeeded (sender, eventArgs);
                break;

            case Language.JS:
                CSharpAutoIndentNeeded (sender, eventArgs);
                break; //JS like C#

            case Language.Lua:
                LuaAutoIndentNeeded (sender, eventArgs);
                break;
        }
    }

    /// <summary>
    ///
    /// </summary>
    protected void PHPAutoIndentNeeded
        (
            object sender,
            AutoIndentEventArgs args
        )
    {
        /*
        FastColoredTextBox tb = sender as FastColoredTextBox;
        tb.CalcAutoIndentShiftByCodeFolding(sender, args);*/
        //block {}
        if (Regex.IsMatch (args.LineText, @"^[^""']*\{.*\}[^""']*$"))
        {
            return;
        }

        //start of block {}
        if (Regex.IsMatch (args.LineText, @"^[^""']*\{"))
        {
            args.ShiftNextLines = args.TabLength;
            return;
        }

        //end of block {}
        if (Regex.IsMatch (args.LineText, @"}[^""']*$"))
        {
            args.Shift = -args.TabLength;
            args.ShiftNextLines = -args.TabLength;
            return;
        }

        //is unclosed operator in previous line ?
        if (Regex.IsMatch (args.PrevLineText, @"^\s*(if|for|foreach|while|[\}\s]*else)\b[^{]*$"))
        {
            if (!Regex.IsMatch (args.PrevLineText, @"(;\s*$)|(;\s*//)")) //operator is unclosed
            {
                args.Shift = args.TabLength;
                return;
            }
        }
    }

    /// <summary>
    ///
    /// </summary>
    protected void SQLAutoIndentNeeded
        (
            object? sender,
            AutoIndentEventArgs args
        )
    {
        var tb = (SyntaxTextBox) sender!;
        tb.CalcAutoIndentShiftByCodeFolding (sender, args);
    }

    /// <summary>
    ///
    /// </summary>
    protected void HTMLAutoIndentNeeded
        (
            object? sender,
            AutoIndentEventArgs args
        )
    {
        var tb = (SyntaxTextBox) sender!;
        tb.CalcAutoIndentShiftByCodeFolding (sender, args);
    }

    /// <summary>
    ///
    /// </summary>
    protected void XMLAutoIndentNeeded
        (
            object sender,
            AutoIndentEventArgs args
        )
    {
        var tb = (SyntaxTextBox) sender!;
        tb.CalcAutoIndentShiftByCodeFolding (sender, args);
    }

    /// <summary>
    ///
    /// </summary>
    protected void VBAutoIndentNeeded
        (
            object? sender,
            AutoIndentEventArgs args
        )
    {
        //end of block
        if (Regex.IsMatch (args.LineText, @"^\s*(End|EndIf|Next|Loop)\b", RegexOptions.IgnoreCase))
        {
            args.Shift = -args.TabLength;
            args.ShiftNextLines = -args.TabLength;
            return;
        }

        //start of declaration
        if (Regex.IsMatch (args.LineText,
                @"\b(Class|Property|Enum|Structure|Sub|Function|Namespace|Interface|Get)\b|(Set\s*\()",
                RegexOptions.IgnoreCase))
        {
            args.ShiftNextLines = args.TabLength;
            return;
        }

        // then ...
        if (Regex.IsMatch (args.LineText, @"\b(Then)\s*\S+", RegexOptions.IgnoreCase))
        {
            return;
        }

        //start of operator block
        if (Regex.IsMatch (args.LineText, @"^\s*(If|While|For|Do|Try|With|Using|Select)\b", RegexOptions.IgnoreCase))
        {
            args.ShiftNextLines = args.TabLength;
            return;
        }

        //Statements else, elseif, case etc
        if (Regex.IsMatch (args.LineText, @"^\s*(Else|ElseIf|Case|Catch|Finally)\b", RegexOptions.IgnoreCase))
        {
            args.Shift = -args.TabLength;
            return;
        }

        //Char _
        if (args.PrevLineText.TrimEnd().EndsWith ("_"))
        {
            args.Shift = args.TabLength;
            return;
        }
    }

    /// <summary>
    ///
    /// </summary>
    protected void CSharpAutoIndentNeeded
        (
            object? sender,
            AutoIndentEventArgs args
        )
    {
        //block {}
        if (Regex.IsMatch (args.LineText, @"^[^""']*\{.*\}[^""']*$"))
        {
            return;
        }

        //start of block {}
        if (Regex.IsMatch (args.LineText, @"^[^""']*\{"))
        {
            args.ShiftNextLines = args.TabLength;
            return;
        }

        //end of block {}
        if (Regex.IsMatch (args.LineText, @"}[^""']*$"))
        {
            args.Shift = -args.TabLength;
            args.ShiftNextLines = -args.TabLength;
            return;
        }

        //label
        if (Regex.IsMatch (args.LineText, @"^\s*\w+\s*:\s*($|//)") &&
            !Regex.IsMatch (args.LineText, @"^\s*default\s*:"))
        {
            args.Shift = -args.TabLength;
            return;
        }

        //some statements: case, default
        if (Regex.IsMatch (args.LineText, @"^\s*(case|default)\b.*:\s*($|//)"))
        {
            args.Shift = -args.TabLength / 2;
            return;
        }

        //is unclosed operator in previous line ?
        if (Regex.IsMatch (args.PrevLineText, @"^\s*(if|for|foreach|while|[\}\s]*else)\b[^{]*$"))
        {
            if (!Regex.IsMatch (args.PrevLineText, @"(;\s*$)|(;\s*//)")) //operator is unclosed
            {
                args.Shift = args.TabLength;
            }
        }
    }

    /// <summary>
    /// Uses the given <paramref name="doc"/> to parse a XML description and adds it as syntax descriptor.
    /// The syntax descriptor is used for highlighting when
    /// <list type="bullet">
    ///     <item>Language property of FCTB is set to <see cref="Language.Custom"/></item>
    ///     <item>DescriptionFile property of FCTB has the same value as the method parameter <paramref name="descriptionFileName"/></item>
    /// </list>
    /// </summary>
    /// <param name="descriptionFileName">Name of the description file</param>
    /// <param name="doc">XmlDocument to parse</param>
    public virtual void AddXmlDescription (string descriptionFileName, XmlDocument doc)
    {
        var desc = ParseXmlDescription (doc);
        descByXMLfileNames[descriptionFileName] = desc;
    }

    /// <summary>
    /// Adds the given <paramref name="style"/> as resilient style. A resilient style is additionally available when highlighting is
    /// based on a syntax descriptor that has been derived from a XML description file. In the run of the highlighting routine
    /// the styles used by the FCTB are always dropped and replaced with the (initial) ones from the syntax descriptor. Resilient styles are
    /// added afterwards and can be used anyway.
    /// </summary>
    /// <param name="style">Style to add</param>
    public virtual void AddResilientStyle (Style style)
    {
        if (resilientStyles.Contains (style))
        {
            return;
        }

        currentTb.CheckStylesBufferSize(); // Prevent buffer overflow
        resilientStyles.Add (style);
    }

    public static SyntaxDescriptor ParseXmlDescription (XmlDocument doc)
    {
        var desc = new SyntaxDescriptor();
        var brackets = doc.SelectSingleNode ("doc/brackets");
        if (brackets != null)
        {
            if (brackets.Attributes!["left"] == null || brackets.Attributes["right"] == null ||
                brackets.Attributes["left"]!.Value == "" || brackets.Attributes["right"]!.Value == "")
            {
                desc.leftBracket = '\x0';
                desc.rightBracket = '\x0';
            }
            else
            {
                desc.leftBracket = brackets.Attributes["left"]!.Value[0];
                desc.rightBracket = brackets.Attributes["right"]!.Value[0];
            }

            if (brackets.Attributes["left2"] == null || brackets.Attributes["right2"] == null ||
                brackets.Attributes["left2"]!.Value == "" || brackets.Attributes["right2"]!.Value == "")
            {
                desc.leftBracket2 = '\x0';
                desc.rightBracket2 = '\x0';
            }
            else
            {
                desc.leftBracket2 = brackets.Attributes["left2"]!.Value[0];
                desc.rightBracket2 = brackets.Attributes["right2"]!.Value[0];
            }

            if (brackets.Attributes["strategy"] == null || brackets.Attributes["strategy"]!.Value == "")
            {
                desc.bracketsHighlightStrategy = BracketsHighlightStrategy.Strategy2;
            }
            else
            {
                desc.bracketsHighlightStrategy =
                    (BracketsHighlightStrategy)Enum.Parse (typeof (BracketsHighlightStrategy),
                        brackets.Attributes["strategy"]!.Value);
            }
        }

        var styleByName = new Dictionary<string, Style>();

        foreach (XmlNode style in doc.SelectNodes ("doc/style")!)
        {
            var s = ParseStyle (style);
            styleByName[style.Attributes!["name"]!.Value] = s;
            desc.styles.Add (s);
        }

        foreach (XmlNode rule in doc.SelectNodes ("doc/rule")!)
        {
            desc.rules.Add (ParseRule (rule, styleByName));
        }

        foreach (XmlNode folding in doc.SelectNodes ("doc/folding")!)
        {
            desc.foldings.Add (ParseFolding (folding));
        }

        return desc;
    }

    /// <summary>
    ///
    /// </summary>
    protected static FoldingDesc ParseFolding (XmlNode foldingNode)
    {
        var folding = new FoldingDesc
        {
            //regex
            startMarkerRegex = foldingNode.Attributes!["start"]!.Value,
            finishMarkerRegex = foldingNode.Attributes["finish"]!.Value
        };

        //options
        var optionsA = foldingNode.Attributes["options"];
        if (optionsA != null)
        {
            folding.options = (RegexOptions)Enum.Parse (typeof (RegexOptions), optionsA.Value);
        }

        return folding;
    }

    /// <summary>
    ///
    /// </summary>
    protected static RuleDesc ParseRule (XmlNode ruleNode, Dictionary<string, Style> styles)
    {
        var rule = new RuleDesc
        {
            pattern = ruleNode.InnerText
        };

        //
        var styleA = ruleNode.Attributes!["style"];
        var optionsA = ruleNode.Attributes["options"];

        //Style
        if (styleA == null)
        {
            throw new Exception ("Rule must contain style name.");
        }

        if (!styles.ContainsKey (styleA.Value))
        {
            throw new Exception ("Style '" + styleA.Value + "' is not found.");
        }

        rule.style = styles[styleA.Value];

        //options
        if (optionsA != null)
        {
            rule.options = (RegexOptions)Enum.Parse (typeof (RegexOptions), optionsA.Value);
        }

        return rule;
    }

    /// <summary>
    ///
    /// </summary>
    protected static Style ParseStyle (XmlNode styleNode)
    {
        var typeA = styleNode.Attributes!["type"];
        var colorA = styleNode.Attributes["color"];
        var backColorA = styleNode.Attributes["backColor"];
        var fontStyleA = styleNode.Attributes["fontStyle"];
        var nameA = styleNode.Attributes["name"];

        //colors
        SolidBrush foreBrush = null!;
        if (colorA != null)
        {
            foreBrush = new SolidBrush (ParseColor (colorA.Value));
        }

        SolidBrush backBrush = null!;
        if (backColorA != null)
        {
            backBrush = new SolidBrush (ParseColor (backColorA.Value));
        }

        //fontStyle
        var fontStyle = FontStyle.Regular;
        if (fontStyleA != null)
        {
            fontStyle = (FontStyle)Enum.Parse (typeof (FontStyle), fontStyleA.Value);
        }

        return new TextStyle (foreBrush, backBrush, fontStyle);
    }

    /// <summary>
    ///
    /// </summary>
    protected static Color ParseColor (string s)
    {
        if (s.StartsWith ("#"))
        {
            if (s.Length <= 7)
            {
                return Color.FromArgb (255,
                    Color.FromArgb (Int32.Parse (s.Substring (1), NumberStyles.AllowHexSpecifier)));
            }
            else
            {
                return Color.FromArgb (Int32.Parse (s.Substring (1), NumberStyles.AllowHexSpecifier));
            }
        }
        else
        {
            return Color.FromName (s);
        }
    }

    /// <summary>
    ///
    /// </summary>
    public void HighlightSyntax (SyntaxDescriptor desc, TextRange range)
    {
        //set style order
        range._textBox.ClearStylesBuffer();
        for (var i = 0; i < desc.styles.Count; i++)
            range._textBox.Styles[i] = desc.styles[i];

        // add resilient styles
        var l = desc.styles.Count;
        for (var i = 0; i < resilientStyles.Count; i++)
            range._textBox.Styles[l + i] = resilientStyles[i];

        //brackets
        var oldBrackets = RememberBrackets (range._textBox);
        range._textBox.LeftBracket = desc.leftBracket;
        range._textBox.RightBracket = desc.rightBracket;
        range._textBox.LeftBracket2 = desc.leftBracket2;
        range._textBox.RightBracket2 = desc.rightBracket2;

        //clear styles of range
        range.ClearStyle (desc.styles.ToArray());

        //highlight syntax
        foreach (var rule in desc.rules)
        {
            range.SetStyle (rule.style, rule.Regex);
        }

        //clear folding
        range.ClearFoldingMarkers();

        //folding markers
        foreach (var folding in desc.foldings)
        {
            range.SetFoldingMarkers (folding.startMarkerRegex, folding.finishMarkerRegex, folding.options);
        }

        //
        RestoreBrackets (range._textBox, oldBrackets);
    }

    /// <summary>
    ///
    /// </summary>
    protected void RestoreBrackets (SyntaxTextBox tb, char[] oldBrackets)
    {
        tb.LeftBracket = oldBrackets[0];
        tb.RightBracket = oldBrackets[1];
        tb.LeftBracket2 = oldBrackets[2];
        tb.RightBracket2 = oldBrackets[3];
    }

    /// <summary>
    ///
    /// </summary>
    protected char[] RememberBrackets (SyntaxTextBox tb)
    {
        return new[] { tb.LeftBracket, tb.RightBracket, tb.LeftBracket2, tb.RightBracket2 };
    }

    /// <summary>
    ///
    /// </summary>
    protected void InitCShaprRegex()
    {
        //CSharpStringRegex = new Regex( @"""""|@""""|''|@"".*?""|(?<!@)(?<range>"".*?[^\\]"")|'.*?[^\\]'", RegexCompiledOption);

        CSharpStringRegex =
            new Regex (
                    @"
                            # Character definitions:
                            '
                            (?> # disable backtracking
                              (?:
                                \\[^\r\n]|    # escaped meta char
                                [^'\r\n]      # any character except '
                              )*
                            )
                            '?
                            |
                            # Normal string & verbatim strings definitions:
                            (?<verbatimIdentifier>@)?         # this group matches if it is an verbatim string
                            ""
                            (?> # disable backtracking
                              (?:
                                # match and consume an escaped character including escaped double quote ("") char
                                (?(verbatimIdentifier)        # if it is a verbatim string ...
                                  """"|                         #   then: only match an escaped double quote ("") char
                                  \\.                         #   else: match an escaped sequence
                                )
                                | # OR

                                # match any char except double quote char ("")
                                [^""]
                              )*
                            )
                            ""
                        ",
                    RegexOptions.ExplicitCapture | RegexOptions.Singleline | RegexOptions.IgnorePatternWhitespace |
                    RegexCompiledOption
                ); //thanks to rittergig for this regex

        CSharpCommentRegex1 = new Regex (@"//.*$", RegexOptions.Multiline | RegexCompiledOption);
        CSharpCommentRegex2 = new Regex (@"(/\*.*?\*/)|(/\*.*)", RegexOptions.Singleline | RegexCompiledOption);
        CSharpCommentRegex3 = new Regex (@"(/\*.*?\*/)|(.*\*/)",
            RegexOptions.Singleline | RegexOptions.RightToLeft | RegexCompiledOption);
        CSharpNumberRegex = new Regex (@"\b\d+[\.]?\d*([eE]\-?\d+)?[lLdDfF]?\b|\b0x[a-fA-F\d]+\b",
            RegexCompiledOption);
        CSharpAttributeRegex = new Regex (@"^\s*(?<range>\[.+?\])\s*$", RegexOptions.Multiline | RegexCompiledOption);
        CSharpClassNameRegex = new Regex (@"\b(class|struct|enum|interface)\s+(?<range>\w+?)\b", RegexCompiledOption);
        CSharpKeywordRegex =
            new Regex (
                @"\b(abstract|add|alias|as|ascending|async|await|base|bool|break|by|byte|case|catch|char|checked|class|const|continue|decimal|default|delegate|descending|do|double|dynamic|else|enum|equals|event|explicit|extern|false|finally|fixed|float|for|foreach|from|get|global|goto|group|if|implicit|in|int|interface|internal|into|is|join|let|lock|long|nameof|namespace|new|null|object|on|operator|orderby|out|override|params|partial|private|protected|public|readonly|ref|remove|return|sbyte|sealed|select|set|short|sizeof|stackalloc|static|static|string|struct|switch|this|throw|true|try|typeof|uint|ulong|unchecked|unsafe|ushort|using|using|value|var|virtual|void|volatile|when|where|while|yield)\b|#region\b|#endregion\b",
                RegexCompiledOption);
    }

    /// <summary>
    ///
    /// </summary>
    public void InitStyleSchema (Language lang)
    {
        switch (lang)
        {
            case Language.CSharp:
                StringStyle = BrownStyle;
                CommentStyle = GreenStyle;
                NumberStyle = MagentaStyle;
                AttributeStyle = GreenStyle;
                ClassNameStyle = BoldStyle;
                KeywordStyle = BlueStyle;
                CommentTagStyle = GrayStyle;
                break;

            case Language.VB:
                StringStyle = BrownStyle;
                CommentStyle = GreenStyle;
                NumberStyle = MagentaStyle;
                ClassNameStyle = BoldStyle;
                KeywordStyle = BlueStyle;
                break;

            case Language.HTML:
                CommentStyle = GreenStyle;
                TagBracketStyle = BlueStyle;
                TagNameStyle = MaroonStyle;
                AttributeStyle = RedStyle;
                AttributeValueStyle = BlueStyle;
                HtmlEntityStyle = RedStyle;
                break;

            case Language.XML:
                CommentStyle = GreenStyle;
                XmlTagBracketStyle = BlueStyle;
                XmlTagNameStyle = MaroonStyle;
                XmlAttributeStyle = RedStyle;
                XmlAttributeValueStyle = BlueStyle;
                XmlEntityStyle = RedStyle;
                XmlCDataStyle = BlackStyle;
                break;

            case Language.JS:
                StringStyle = BrownStyle;
                CommentStyle = GreenStyle;
                NumberStyle = MagentaStyle;
                KeywordStyle = BlueStyle;
                break;

            case Language.Lua:
                StringStyle = BrownStyle;
                CommentStyle = GreenStyle;
                NumberStyle = MagentaStyle;
                KeywordStyle = BlueBoldStyle;
                FunctionsStyle = MaroonStyle;
                break;

            case Language.PHP:
                StringStyle = RedStyle;
                CommentStyle = GreenStyle;
                NumberStyle = RedStyle;
                VariableStyle = MaroonStyle;
                KeywordStyle = MagentaStyle;
                KeywordStyle2 = BlueStyle;
                KeywordStyle3 = GrayStyle;
                break;

            case Language.SQL:
                StringStyle = RedStyle;
                CommentStyle = GreenStyle;
                NumberStyle = MagentaStyle;
                KeywordStyle = BlueBoldStyle;
                StatementsStyle = BlueBoldStyle;
                FunctionsStyle = MaroonStyle;
                VariableStyle = MaroonStyle;
                TypesStyle = BrownStyle;
                break;

            case Language.JSON:
                StringStyle = BrownStyle;
                NumberStyle = MagentaStyle;
                KeywordStyle = BlueStyle;
                break;
        }
    }

    /// <summary>
    /// Highlights C# code
    /// </summary>
    /// <param name="range"></param>
    public virtual void CSharpSyntaxHighlight (TextRange range)
    {
        range._textBox.CommentPrefix = "//";
        range._textBox.LeftBracket = '(';
        range._textBox.RightBracket = ')';
        range._textBox.LeftBracket2 = '{';
        range._textBox.RightBracket2 = '}';
        range._textBox.BracketsHighlightStrategy = BracketsHighlightStrategy.Strategy2;

        range._textBox.AutoIndentCharsPatterns
            = @"
^\s*[\w\.]+(\s\w+)?\s*(?<range>=)\s*(?<range>[^;=]+);
^\s*(case|default)\s*[^:]*(?<range>:)\s*(?<range>[^;]+);
";

        //clear style of changed range
        range.ClearStyle (StringStyle, CommentStyle, NumberStyle, AttributeStyle, ClassNameStyle, KeywordStyle);

        //
        if (CSharpStringRegex == null)
        {
            InitCShaprRegex();
        }

        //string highlighting
        range.SetStyle (StringStyle, CSharpStringRegex);

        //comment highlighting
        range.SetStyle (CommentStyle, CSharpCommentRegex1);
        range.SetStyle (CommentStyle, CSharpCommentRegex2);
        range.SetStyle (CommentStyle, CSharpCommentRegex3);

        //number highlighting
        range.SetStyle (NumberStyle, CSharpNumberRegex);

        //attribute highlighting
        range.SetStyle (AttributeStyle, CSharpAttributeRegex);

        //class name highlighting
        range.SetStyle (ClassNameStyle, CSharpClassNameRegex);

        //keyword highlighting
        range.SetStyle (KeywordStyle, CSharpKeywordRegex);

        //find document comments
        foreach (var r in range.GetRanges (@"^\s*///.*$", RegexOptions.Multiline))
        {
            //remove C# highlighting from this fragment
            r.ClearStyle (StyleIndex.All);

            //do XML highlighting
            if (HTMLTagRegex == null)
            {
                InitHTMLRegex();
            }

            //
            r.SetStyle (CommentStyle);

            //tags
            foreach (var rr in r.GetRanges (HTMLTagContentRegex))
            {
                rr.ClearStyle (StyleIndex.All);
                rr.SetStyle (CommentTagStyle);
            }

            //prefix '///'
            foreach (var rr in r.GetRanges (@"^\s*///", RegexOptions.Multiline))
            {
                rr.ClearStyle (StyleIndex.All);
                rr.SetStyle (CommentTagStyle);
            }
        }

        //clear folding markers
        range.ClearFoldingMarkers();

        //set folding markers
        range.SetFoldingMarkers ("{", "}"); //allow to collapse brackets block
        range.SetFoldingMarkers (@"#region\b", @"#endregion\b"); //allow to collapse #region blocks
        range.SetFoldingMarkers (@"/\*", @"\*/"); //allow to collapse comment block
    }

    /// <summary>
    ///
    /// </summary>
    protected void InitVBRegex()
    {
        VBStringRegex = new Regex (@"""""|"".*?[^\\]""", RegexCompiledOption);
        VBCommentRegex = new Regex (@"'.*$", RegexOptions.Multiline | RegexCompiledOption);
        VBNumberRegex = new Regex (@"\b\d+[\.]?\d*([eE]\-?\d+)?\b", RegexCompiledOption);
        VBClassNameRegex = new Regex (@"\b(Class|Structure|Enum|Interface)[ ]+(?<range>\w+?)\b",
            RegexOptions.IgnoreCase | RegexCompiledOption);
        VBKeywordRegex =
            new Regex (
                @"\b(AddHandler|AddressOf|Alias|And|AndAlso|As|Boolean|ByRef|Byte|ByVal|Call|Case|Catch|CBool|CByte|CChar|CDate|CDbl|CDec|Char|CInt|Class|CLng|CObj|Const|Continue|CSByte|CShort|CSng|CStr|CType|CUInt|CULng|CUShort|Date|Decimal|Declare|Default|Delegate|Dim|DirectCast|Do|Double|Each|Else|ElseIf|End|EndIf|Enum|Erase|Error|Event|Exit|False|Finally|For|Friend|Function|Get|GetType|GetXMLNamespace|Global|GoSub|GoTo|Handles|If|Implements|Imports|In|Inherits|Integer|Interface|Is|IsNot|Let|Lib|Like|Long|Loop|Me|Mod|Module|MustInherit|MustOverride|MyBase|MyClass|Namespace|Narrowing|New|Next|Not|Nothing|NotInheritable|NotOverridable|Object|Of|On|Operator|Option|Optional|Or|OrElse|Overloads|Overridable|Overrides|ParamArray|Partial|Private|Property|Protected|Public|RaiseEvent|ReadOnly|ReDim|REM|RemoveHandler|Resume|Return|SByte|Select|Set|Shadows|Shared|Short|Single|Static|Step|Stop|String|Structure|Sub|SyncLock|Then|Throw|To|True|Try|TryCast|TypeOf|UInteger|ULong|UShort|Using|Variant|Wend|When|While|Widening|With|WithEvents|WriteOnly|Xor|Region)\b|(#Const|#Else|#ElseIf|#End|#If|#Region)\b",
                RegexOptions.IgnoreCase | RegexCompiledOption);
    }

    /// <summary>
    /// Highlights VB code
    /// </summary>
    /// <param name="range"></param>
    public virtual void VBSyntaxHighlight (TextRange range)
    {
        range._textBox.CommentPrefix = "'";
        range._textBox.LeftBracket = '(';
        range._textBox.RightBracket = ')';
        range._textBox.LeftBracket2 = '\x0';
        range._textBox.RightBracket2 = '\x0';

        range._textBox.AutoIndentCharsPatterns
            = @"
^\s*[\w\.\(\)]+\s*(?<range>=)\s*(?<range>.+)
";

        //clear style of changed range
        range.ClearStyle (StringStyle, CommentStyle, NumberStyle, ClassNameStyle, KeywordStyle);

        //
        if (VBStringRegex == null)
        {
            InitVBRegex();
        }

        //string highlighting
        range.SetStyle (StringStyle, VBStringRegex);

        //comment highlighting
        range.SetStyle (CommentStyle, VBCommentRegex);

        //number highlighting
        range.SetStyle (NumberStyle, VBNumberRegex);

        //class name highlighting
        range.SetStyle (ClassNameStyle, VBClassNameRegex);

        //keyword highlighting
        range.SetStyle (KeywordStyle, VBKeywordRegex);

        //clear folding markers
        range.ClearFoldingMarkers();

        //set folding markers
        range.SetFoldingMarkers (@"#Region\b", @"#End\s+Region\b", RegexOptions.IgnoreCase);
        range.SetFoldingMarkers (@"\b(Class|Property|Enum|Structure|Interface)[ \t]+\S+",
            @"\bEnd (Class|Property|Enum|Structure|Interface)\b", RegexOptions.IgnoreCase);
        range.SetFoldingMarkers (@"^\s*(?<range>While)[ \t]+\S+", @"^\s*(?<range>End While)\b",
            RegexOptions.Multiline | RegexOptions.IgnoreCase);
        range.SetFoldingMarkers (@"\b(Sub|Function)[ \t]+[^\s']+", @"\bEnd (Sub|Function)\b", RegexOptions.IgnoreCase);

        //this declared separately because Sub and Function can be unclosed
        range.SetFoldingMarkers (@"(\r|\n|^)[ \t]*(?<range>Get|Set)[ \t]*(\r|\n|$)", @"\bEnd (Get|Set)\b",
            RegexOptions.IgnoreCase);
        range.SetFoldingMarkers (@"^\s*(?<range>For|For\s+Each)\b", @"^\s*(?<range>Next)\b",
            RegexOptions.Multiline | RegexOptions.IgnoreCase);
        range.SetFoldingMarkers (@"^\s*(?<range>Do)\b", @"^\s*(?<range>Loop)\b",
            RegexOptions.Multiline | RegexOptions.IgnoreCase);
    }

    /// <summary>
    ///
    /// </summary>
    protected void InitHTMLRegex()
    {
        HTMLCommentRegex1 = new Regex (@"(<!--.*?-->)|(<!--.*)", RegexOptions.Singleline | RegexCompiledOption);
        HTMLCommentRegex2 = new Regex (@"(<!--.*?-->)|(.*-->)",
            RegexOptions.Singleline | RegexOptions.RightToLeft | RegexCompiledOption);
        HTMLTagRegex = new Regex (@"<|/>|</|>", RegexCompiledOption);
        HTMLTagNameRegex = new Regex (@"<(?<range>[!\w:]+)", RegexCompiledOption);
        HTMLEndTagRegex = new Regex (@"</(?<range>[\w:]+)>", RegexCompiledOption);
        HTMLTagContentRegex = new Regex (@"<[^>]+>", RegexCompiledOption);
        HTMLAttrRegex =
            new Regex (
                @"(?<range>[\w\d\-]{1,20}?)='[^']*'|(?<range>[\w\d\-]{1,20})=""[^""]*""|(?<range>[\w\d\-]{1,20})=[\w\d\-]{1,20}",
                RegexCompiledOption);
        HTMLAttrValRegex =
            new Regex (
                @"[\w\d\-]{1,20}?=(?<range>'[^']*')|[\w\d\-]{1,20}=(?<range>""[^""]*"")|[\w\d\-]{1,20}=(?<range>[\w\d\-]{1,20})",
                RegexCompiledOption);
        HTMLEntityRegex = new Regex (@"\&(amp|gt|lt|nbsp|quot|apos|copy|reg|#[0-9]{1,8}|#x[0-9a-f]{1,8});",
            RegexCompiledOption | RegexOptions.IgnoreCase);
    }

    /// <summary>
    /// Highlights HTML code
    /// </summary>
    /// <param name="range"></param>
    public virtual void HTMLSyntaxHighlight (TextRange range)
    {
        range._textBox.CommentPrefix = null;
        range._textBox.LeftBracket = '<';
        range._textBox.RightBracket = '>';
        range._textBox.LeftBracket2 = '(';
        range._textBox.RightBracket2 = ')';
        range._textBox.AutoIndentCharsPatterns = @"";

        //clear style of changed range
        range.ClearStyle (CommentStyle, TagBracketStyle, TagNameStyle, AttributeStyle, AttributeValueStyle,
            HtmlEntityStyle);

        //
        if (HTMLTagRegex == null)
        {
            InitHTMLRegex();
        }

        //comment highlighting
        range.SetStyle (CommentStyle, HTMLCommentRegex1);
        range.SetStyle (CommentStyle, HTMLCommentRegex2);

        //tag brackets highlighting
        range.SetStyle (TagBracketStyle, HTMLTagRegex);

        //tag name
        range.SetStyle (TagNameStyle, HTMLTagNameRegex);

        //end of tag
        range.SetStyle (TagNameStyle, HTMLEndTagRegex);

        //attributes
        range.SetStyle (AttributeStyle, HTMLAttrRegex);

        //attribute values
        range.SetStyle (AttributeValueStyle, HTMLAttrValRegex);

        //html entity
        range.SetStyle (HtmlEntityStyle, HTMLEntityRegex);

        //clear folding markers
        range.ClearFoldingMarkers();

        //set folding markers
        range.SetFoldingMarkers ("<head", "</head>", RegexOptions.IgnoreCase);
        range.SetFoldingMarkers ("<body", "</body>", RegexOptions.IgnoreCase);
        range.SetFoldingMarkers ("<table", "</table>", RegexOptions.IgnoreCase);
        range.SetFoldingMarkers ("<form", "</form>", RegexOptions.IgnoreCase);
        range.SetFoldingMarkers ("<div", "</div>", RegexOptions.IgnoreCase);
        range.SetFoldingMarkers ("<script", "</script>", RegexOptions.IgnoreCase);
        range.SetFoldingMarkers ("<tr", "</tr>", RegexOptions.IgnoreCase);
    }

    /// <summary>
    ///
    /// </summary>
    protected void InitXMLRegex()
    {
        XMLCommentRegex1 = new Regex (@"(<!--.*?-->)|(<!--.*)", RegexOptions.Singleline | RegexCompiledOption);
        XMLCommentRegex2 = new Regex (@"(<!--.*?-->)|(.*-->)",
            RegexOptions.Singleline | RegexOptions.RightToLeft | RegexCompiledOption);

        XMLTagRegex = new Regex (@"<\?|</|>|<|/>|\?>", RegexCompiledOption);
        XMLTagNameRegex = new Regex (@"<[?](?<range1>[x][m][l]{1})|<(?<range>[!\w:\-\.]+)", RegexCompiledOption);
        XMLEndTagRegex = new Regex (@"</(?<range>[\w:\-\.]+)>", RegexCompiledOption);

        XMLTagContentRegex = new Regex (@"<[^>]+>", RegexCompiledOption);
        XMLAttrRegex =
            new Regex (
                @"(?<range>[\w\d\-\:]+)[ ]*=[ ]*'[^']*'|(?<range>[\w\d\-\:]+)[ ]*=[ ]*""[^""]*""|(?<range>[\w\d\-\:]+)[ ]*=[ ]*[\w\d\-\:]+",
                RegexCompiledOption);
        XMLAttrValRegex =
            new Regex (
                @"[\w\d\-]+?=(?<range>'[^']*')|[\w\d\-]+[ ]*=[ ]*(?<range>""[^""]*"")|[\w\d\-]+[ ]*=[ ]*(?<range>[\w\d\-]+)",
                RegexCompiledOption);
        XMLEntityRegex = new Regex (@"\&(amp|gt|lt|nbsp|quot|apos|copy|reg|#[0-9]{1,8}|#x[0-9a-f]{1,8});",
            RegexCompiledOption | RegexOptions.IgnoreCase);
        XMLCDataRegex = new Regex (@"<!\s*\[CDATA\s*\[(?<text>(?>[^]]+|](?!]>))*)]]>",
            RegexCompiledOption |
            RegexOptions
                .IgnoreCase); // http://stackoverflow.com/questions/21681861/i-need-a-regex-that-matches-cdata-elements-in-html
        XMLFoldingRegex = new Regex (@"<(?<range>/?[\w:\-\.]+)\s[^>]*?[^/]>|<(?<range>/?[\w:\-\.]+)\s*>",
            RegexOptions.Singleline | RegexCompiledOption);
    }

    /// <summary>
    /// Highlights XML code
    /// </summary>
    /// <param name="range"></param>
    public virtual void XMLSyntaxHighlight (TextRange range)
    {
        range._textBox.CommentPrefix = null;
        range._textBox.LeftBracket = '<';
        range._textBox.RightBracket = '>';
        range._textBox.LeftBracket2 = '(';
        range._textBox.RightBracket2 = ')';
        range._textBox.AutoIndentCharsPatterns = @"";

        //clear style of changed range
        range.ClearStyle (CommentStyle, XmlTagBracketStyle, XmlTagNameStyle, XmlAttributeStyle, XmlAttributeValueStyle,
            XmlEntityStyle, XmlCDataStyle);

        //
        if (XMLTagRegex == null)
        {
            InitXMLRegex();
        }

        //xml CData
        range.SetStyle (XmlCDataStyle, XMLCDataRegex);

        //comment highlighting
        range.SetStyle (CommentStyle, XMLCommentRegex1);
        range.SetStyle (CommentStyle, XMLCommentRegex2);

        //tag brackets highlighting
        range.SetStyle (XmlTagBracketStyle, XMLTagRegex);

        //tag name
        range.SetStyle (XmlTagNameStyle, XMLTagNameRegex);

        //end of tag
        range.SetStyle (XmlTagNameStyle, XMLEndTagRegex);

        //attributes
        range.SetStyle (XmlAttributeStyle, XMLAttrRegex);

        //attribute values
        range.SetStyle (XmlAttributeValueStyle, XMLAttrValRegex);

        //xml entity
        range.SetStyle (XmlEntityStyle, XMLEntityRegex);

        //clear folding markers
        range.ClearFoldingMarkers();

        //set folding markers
        XmlFolding (range);
    }

    private void XmlFolding (TextRange range)
    {
        var stack = new Stack<XmlFoldingTag>();
        var id = 0;
        var fctb = range._textBox;

        //extract opening and closing tags (exclude open-close tags: <TAG/>)
        foreach (var r in range.GetRanges (XMLFoldingRegex))
        {
            var tagName = r.Text;
            var iLine = r.Start.Line;

            //if it is opening tag...
            if (tagName[0] != '/')
            {
                // ...push into stack
                var tag = new XmlFoldingTag { Name = tagName, id = id++, startLine = r.Start.Line };
                stack.Push (tag);

                // if this line has no markers - set marker
                if (string.IsNullOrEmpty (fctb[iLine].FoldingStartMarker))
                {
                    fctb[iLine].FoldingStartMarker = tag.Marker;
                }
            }
            else
            {
                //if it is closing tag - pop from stack
                if (stack.Count > 0)
                {
                    var tag = stack.Pop();

                    //compare line number
                    if (iLine == tag.startLine)
                    {
                        //remove marker, because same line can not be folding
                        if (fctb[iLine].FoldingStartMarker == tag.Marker) //was it our marker?
                        {
                            fctb[iLine].FoldingStartMarker = null;
                        }
                    }
                    else
                    {
                        //set end folding marker
                        if (string.IsNullOrEmpty (fctb[iLine].FoldingEndMarker))
                        {
                            fctb[iLine].FoldingEndMarker = tag.Marker;
                        }
                    }
                }
            }
        }
    }

    class XmlFoldingTag
    {
        public string Name;
        public int id;
        public int startLine;

        public string Marker
        {
            get { return Name + id; }
        }
    }

    /// <summary>
    ///
    /// </summary>
    protected void InitSQLRegex()
    {
        SQLStringRegex = new Regex (@"""""|''|"".*?[^\\]""|'.*?[^\\]'", RegexCompiledOption);
        SQLNumberRegex = new Regex (@"\b\d+[\.]?\d*([eE]\-?\d+)?\b", RegexCompiledOption);
        SQLCommentRegex1 = new Regex (@"--.*$", RegexOptions.Multiline | RegexCompiledOption);
        SQLCommentRegex2 = new Regex (@"(/\*.*?\*/)|(/\*.*)", RegexOptions.Singleline | RegexCompiledOption);
        SQLCommentRegex3 = new Regex (@"(/\*.*?\*/)|(.*\*/)",
            RegexOptions.Singleline | RegexOptions.RightToLeft | RegexCompiledOption);
        SQLCommentRegex4 = new Regex (@"#.*$", RegexOptions.Multiline | RegexCompiledOption);
        SQLVarRegex = new Regex (@"@[a-zA-Z_\d]*\b", RegexCompiledOption);
        SQLStatementsRegex = new Regex (
            @"\b(ALTER APPLICATION ROLE|ALTER ASSEMBLY|ALTER ASYMMETRIC KEY|ALTER AUTHORIZATION|ALTER BROKER PRIORITY|ALTER CERTIFICATE|ALTER CREDENTIAL|ALTER CRYPTOGRAPHIC PROVIDER|ALTER DATABASE|ALTER DATABASE AUDIT SPECIFICATION|ALTER DATABASE ENCRYPTION KEY|ALTER ENDPOINT|ALTER EVENT SESSION|ALTER FULLTEXT CATALOG|ALTER FULLTEXT INDEX|ALTER FULLTEXT STOPLIST|ALTER FUNCTION|ALTER INDEX|ALTER LOGIN|ALTER MASTER KEY|ALTER MESSAGE TYPE|ALTER PARTITION FUNCTION|ALTER PARTITION SCHEME|ALTER PROCEDURE|ALTER QUEUE|ALTER REMOTE SERVICE BINDING|ALTER RESOURCE GOVERNOR|ALTER RESOURCE POOL|ALTER ROLE|ALTER ROUTE|ALTER SCHEMA|ALTER SERVER AUDIT|ALTER SERVER AUDIT SPECIFICATION|ALTER SERVICE|ALTER SERVICE MASTER KEY|ALTER SYMMETRIC KEY|ALTER TABLE|ALTER TRIGGER|ALTER USER|ALTER VIEW|ALTER WORKLOAD GROUP|ALTER XML SCHEMA COLLECTION|BULK INSERT|CREATE AGGREGATE|CREATE APPLICATION ROLE|CREATE ASSEMBLY|CREATE ASYMMETRIC KEY|CREATE BROKER PRIORITY|CREATE CERTIFICATE|CREATE CONTRACT|CREATE CREDENTIAL|CREATE CRYPTOGRAPHIC PROVIDER|CREATE DATABASE|CREATE DATABASE AUDIT SPECIFICATION|CREATE DATABASE ENCRYPTION KEY|CREATE DEFAULT|CREATE ENDPOINT|CREATE EVENT NOTIFICATION|CREATE EVENT SESSION|CREATE FULLTEXT CATALOG|CREATE FULLTEXT INDEX|CREATE FULLTEXT STOPLIST|CREATE FUNCTION|CREATE INDEX|CREATE LOGIN|CREATE MASTER KEY|CREATE MESSAGE TYPE|CREATE PARTITION FUNCTION|CREATE PARTITION SCHEME|CREATE PROCEDURE|CREATE QUEUE|CREATE REMOTE SERVICE BINDING|CREATE RESOURCE POOL|CREATE ROLE|CREATE ROUTE|CREATE RULE|CREATE SCHEMA|CREATE SERVER AUDIT|CREATE SERVER AUDIT SPECIFICATION|CREATE SERVICE|CREATE SPATIAL INDEX|CREATE STATISTICS|CREATE SYMMETRIC KEY|CREATE SYNONYM|CREATE TABLE|CREATE TRIGGER|CREATE TYPE|CREATE USER|CREATE VIEW|CREATE WORKLOAD GROUP|CREATE XML INDEX|CREATE XML SCHEMA COLLECTION|DELETE|DISABLE TRIGGER|DROP AGGREGATE|DROP APPLICATION ROLE|DROP ASSEMBLY|DROP ASYMMETRIC KEY|DROP BROKER PRIORITY|DROP CERTIFICATE|DROP CONTRACT|DROP CREDENTIAL|DROP CRYPTOGRAPHIC PROVIDER|DROP DATABASE|DROP DATABASE AUDIT SPECIFICATION|DROP DATABASE ENCRYPTION KEY|DROP DEFAULT|DROP ENDPOINT|DROP EVENT NOTIFICATION|DROP EVENT SESSION|DROP FULLTEXT CATALOG|DROP FULLTEXT INDEX|DROP FULLTEXT STOPLIST|DROP FUNCTION|DROP INDEX|DROP LOGIN|DROP MASTER KEY|DROP MESSAGE TYPE|DROP PARTITION FUNCTION|DROP PARTITION SCHEME|DROP PROCEDURE|DROP QUEUE|DROP REMOTE SERVICE BINDING|DROP RESOURCE POOL|DROP ROLE|DROP ROUTE|DROP RULE|DROP SCHEMA|DROP SERVER AUDIT|DROP SERVER AUDIT SPECIFICATION|DROP SERVICE|DROP SIGNATURE|DROP STATISTICS|DROP SYMMETRIC KEY|DROP SYNONYM|DROP TABLE|DROP TRIGGER|DROP TYPE|DROP USER|DROP VIEW|DROP WORKLOAD GROUP|DROP XML SCHEMA COLLECTION|ENABLE TRIGGER|EXEC|EXECUTE|REPLACE|FROM|INSERT|MERGE|OPTION|OUTPUT|SELECT|TOP|TRUNCATE TABLE|UPDATE|UPDATE STATISTICS|WHERE|WITH|INTO|IN|SET)\b",
            RegexOptions.IgnoreCase | RegexCompiledOption);
        SQLKeywordsRegex = new Regex (
            @"\b(ADD|ALL|AND|ANY|AS|ASC|AUTHORIZATION|BACKUP|BEGIN|BETWEEN|BREAK|BROWSE|BY|CASCADE|CHECK|CHECKPOINT|CLOSE|CLUSTERED|COLLATE|COLUMN|COMMIT|COMPUTE|CONSTRAINT|CONTAINS|CONTINUE|CROSS|CURRENT|CURRENT_DATE|CURRENT_TIME|CURSOR|DATABASE|DBCC|DEALLOCATE|DECLARE|DEFAULT|DENY|DESC|DISK|DISTINCT|DISTRIBUTED|DOUBLE|DUMP|ELSE|END|ERRLVL|ESCAPE|EXCEPT|EXISTS|EXIT|EXTERNAL|FETCH|FILE|FILLFACTOR|FOR|FOREIGN|FREETEXT|FULL|FUNCTION|GOTO|GRANT|GROUP|HAVING|HOLDLOCK|IDENTITY|IDENTITY_INSERT|IDENTITYCOL|IF|INDEX|INNER|INTERSECT|IS|JOIN|KEY|KILL|LIKE|LINENO|LOAD|NATIONAL|NOCHECK|NONCLUSTERED|NOT|NULL|OF|OFF|OFFSETS|ON|OPEN|OR|ORDER|OUTER|OVER|PERCENT|PIVOT|PLAN|PRECISION|PRIMARY|PRINT|PROC|PROCEDURE|PUBLIC|RAISERROR|READ|READTEXT|RECONFIGURE|REFERENCES|REPLICATION|RESTORE|RESTRICT|RETURN|REVERT|REVOKE|ROLLBACK|ROWCOUNT|ROWGUIDCOL|RULE|SAVE|SCHEMA|SECURITYAUDIT|SHUTDOWN|SOME|STATISTICS|TABLE|TABLESAMPLE|TEXTSIZE|THEN|TO|TRAN|TRANSACTION|TRIGGER|TSEQUAL|UNION|UNIQUE|UNPIVOT|UPDATETEXT|USE|USER|VALUES|VARYING|VIEW|WAITFOR|WHEN|WHILE|WRITETEXT)\b",
            RegexOptions.IgnoreCase | RegexCompiledOption);
        SQLFunctionsRegex = new Regex (
            @"(@@CONNECTIONS|@@CPU_BUSY|@@CURSOR_ROWS|@@DATEFIRST|@@DATEFIRST|@@DBTS|@@ERROR|@@FETCH_STATUS|@@IDENTITY|@@IDLE|@@IO_BUSY|@@LANGID|@@LANGUAGE|@@LOCK_TIMEOUT|@@MAX_CONNECTIONS|@@MAX_PRECISION|@@NESTLEVEL|@@OPTIONS|@@PACKET_ERRORS|@@PROCID|@@REMSERVER|@@ROWCOUNT|@@SERVERNAME|@@SERVICENAME|@@SPID|@@TEXTSIZE|@@TRANCOUNT|@@VERSION)\b|\b(ABS|ACOS|APP_NAME|ASCII|ASIN|ASSEMBLYPROPERTY|AsymKey_ID|ASYMKEY_ID|asymkeyproperty|ASYMKEYPROPERTY|ATAN|ATN2|AVG|CASE|CAST|CEILING|Cert_ID|Cert_ID|CertProperty|CHAR|CHARINDEX|CHECKSUM_AGG|COALESCE|COL_LENGTH|COL_NAME|COLLATIONPROPERTY|COLLATIONPROPERTY|COLUMNPROPERTY|COLUMNS_UPDATED|COLUMNS_UPDATED|CONTAINSTABLE|CONVERT|COS|COT|COUNT|COUNT_BIG|CRYPT_GEN_RANDOM|CURRENT_TIMESTAMP|CURRENT_TIMESTAMP|CURRENT_USER|CURRENT_USER|CURSOR_STATUS|DATABASE_PRINCIPAL_ID|DATABASE_PRINCIPAL_ID|DATABASEPROPERTY|DATABASEPROPERTYEX|DATALENGTH|DATALENGTH|DATEADD|DATEDIFF|DATENAME|DATEPART|DAY|DB_ID|DB_NAME|DECRYPTBYASYMKEY|DECRYPTBYCERT|DECRYPTBYKEY|DECRYPTBYKEYAUTOASYMKEY|DECRYPTBYKEYAUTOCERT|DECRYPTBYPASSPHRASE|DEGREES|DENSE_RANK|DIFFERENCE|ENCRYPTBYASYMKEY|ENCRYPTBYCERT|ENCRYPTBYKEY|ENCRYPTBYPASSPHRASE|ERROR_LINE|ERROR_MESSAGE|ERROR_NUMBER|ERROR_PROCEDURE|ERROR_SEVERITY|ERROR_STATE|EVENTDATA|EXP|FILE_ID|FILE_IDEX|FILE_NAME|FILEGROUP_ID|FILEGROUP_NAME|FILEGROUPPROPERTY|FILEPROPERTY|FLOOR|fn_helpcollations|fn_listextendedproperty|fn_servershareddrives|fn_virtualfilestats|fn_virtualfilestats|FORMATMESSAGE|FREETEXTTABLE|FULLTEXTCATALOGPROPERTY|FULLTEXTSERVICEPROPERTY|GETANSINULL|GETDATE|GETUTCDATE|GROUPING|HAS_PERMS_BY_NAME|HOST_ID|HOST_NAME|IDENT_CURRENT|IDENT_CURRENT|IDENT_INCR|IDENT_INCR|IDENT_SEED|IDENTITY\(|INDEX_COL|INDEXKEY_PROPERTY|INDEXPROPERTY|IS_MEMBER|IS_OBJECTSIGNED|IS_SRVROLEMEMBER|ISDATE|ISDATE|ISNULL|ISNUMERIC|Key_GUID|Key_GUID|Key_ID|Key_ID|KEY_NAME|KEY_NAME|LEFT|LEN|LOG|LOG10|LOWER|LTRIM|MAX|MIN|MONTH|NCHAR|NEWID|NTILE|NULLIF|OBJECT_DEFINITION|OBJECT_ID|OBJECT_NAME|OBJECT_SCHEMA_NAME|OBJECTPROPERTY|OBJECTPROPERTYEX|OPENDATASOURCE|OPENQUERY|OPENROWSET|OPENXML|ORIGINAL_LOGIN|ORIGINAL_LOGIN|PARSENAME|PATINDEX|PATINDEX|PERMISSIONS|PI|POWER|PUBLISHINGSERVERNAME|PWDCOMPARE|PWDENCRYPT|QUOTENAME|RADIANS|RAND|RANK|REPLICATE|REVERSE|RIGHT|ROUND|ROW_NUMBER|ROWCOUNT_BIG|RTRIM|SCHEMA_ID|SCHEMA_ID|SCHEMA_NAME|SCHEMA_NAME|SCOPE_IDENTITY|SERVERPROPERTY|SESSION_USER|SESSION_USER|SESSIONPROPERTY|SETUSER|SIGN|SignByAsymKey|SignByCert|SIN|SOUNDEX|SPACE|SQL_VARIANT_PROPERTY|SQRT|SQUARE|STATS_DATE|STDEV|STDEVP|STR|STUFF|SUBSTRING|SUM|SUSER_ID|SUSER_NAME|SUSER_SID|SUSER_SNAME|SWITCHOFFSET|SYMKEYPROPERTY|symkeyproperty|sys\.dm_db_index_physical_stats|sys\.fn_builtin_permissions|sys\.fn_my_permissions|SYSDATETIME|SYSDATETIMEOFFSET|SYSTEM_USER|SYSTEM_USER|SYSUTCDATETIME|TAN|TERTIARY_WEIGHTS|TEXTPTR|TODATETIMEOFFSET|TRIGGER_NESTLEVEL|TYPE_ID|TYPE_NAME|TYPEPROPERTY|UNICODE|UPDATE\(|UPPER|USER_ID|USER_NAME|USER_NAME|VAR|VARP|VerifySignedByAsymKey|VerifySignedByCert|XACT_STATE|YEAR)\b",
            RegexOptions.IgnoreCase | RegexCompiledOption);
        SQLTypesRegex =
            new Regex (
                @"\b(BIGINT|NUMERIC|BIT|SMALLINT|DECIMAL|SMALLMONEY|INT|TINYINT|MONEY|FLOAT|REAL|DATE|DATETIMEOFFSET|DATETIME2|SMALLDATETIME|DATETIME|TIME|CHAR|VARCHAR|TEXT|NCHAR|NVARCHAR|NTEXT|BINARY|VARBINARY|IMAGE|TIMESTAMP|HIERARCHYID|TABLE|UNIQUEIDENTIFIER|SQL_VARIANT|XML)\b",
                RegexOptions.IgnoreCase | RegexCompiledOption);
    }

    /// <summary>
    /// Highlights SQL code
    /// </summary>
    /// <param name="range"></param>
    public virtual void SQLSyntaxHighlight (TextRange range)
    {
        range._textBox.CommentPrefix = "--";
        range._textBox.LeftBracket = '(';
        range._textBox.RightBracket = ')';
        range._textBox.LeftBracket2 = '\x0';
        range._textBox.RightBracket2 = '\x0';

        range._textBox.AutoIndentCharsPatterns = @"";

        //clear style of changed range
        range.ClearStyle (CommentStyle, StringStyle, NumberStyle, VariableStyle, StatementsStyle, KeywordStyle,
            FunctionsStyle, TypesStyle);

        //
        if (SQLStringRegex == null)
        {
            InitSQLRegex();
        }

        //comment highlighting
        range.SetStyle (CommentStyle, SQLCommentRegex1);
        range.SetStyle (CommentStyle, SQLCommentRegex2);
        range.SetStyle (CommentStyle, SQLCommentRegex3);
        range.SetStyle (CommentStyle, SQLCommentRegex4);

        //string highlighting
        range.SetStyle (StringStyle, SQLStringRegex);

        //number highlighting
        range.SetStyle (NumberStyle, SQLNumberRegex);

        //types highlighting
        range.SetStyle (TypesStyle, SQLTypesRegex);

        //var highlighting
        range.SetStyle (VariableStyle, SQLVarRegex);

        //statements
        range.SetStyle (StatementsStyle, SQLStatementsRegex);

        //keywords
        range.SetStyle (KeywordStyle, SQLKeywordsRegex);

        //functions
        range.SetStyle (FunctionsStyle, SQLFunctionsRegex);

        //clear folding markers
        range.ClearFoldingMarkers();

        //set folding markers
        range.SetFoldingMarkers (@"\bBEGIN\b", @"\bEND\b", RegexOptions.IgnoreCase);

        //allow to collapse BEGIN..END blocks
        range.SetFoldingMarkers (@"/\*", @"\*/"); //allow to collapse comment block
    }

    /// <summary>
    ///
    /// </summary>
    protected void InitPHPRegex()
    {
        PHPStringRegex = new Regex (@"""""|''|"".*?[^\\]""|'.*?[^\\]'", RegexCompiledOption);
        PHPNumberRegex = new Regex (@"\b\d+[\.]?\d*\b", RegexCompiledOption);
        PHPCommentRegex1 = new Regex (@"(//|#).*$", RegexOptions.Multiline | RegexCompiledOption);
        PHPCommentRegex2 = new Regex (@"(/\*.*?\*/)|(/\*.*)", RegexOptions.Singleline | RegexCompiledOption);
        PHPCommentRegex3 = new Regex (@"(/\*.*?\*/)|(.*\*/)",
            RegexOptions.Singleline | RegexOptions.RightToLeft | RegexCompiledOption);
        PHPVarRegex = new Regex (@"\$[a-zA-Z_\d]*\b", RegexCompiledOption);
        PHPKeywordRegex1 =
            new Regex (
                @"\b(die|echo|empty|exit|eval|include|include_once|isset|list|require|require_once|return|print|unset)\b",
                RegexCompiledOption);
        PHPKeywordRegex2 =
            new Regex (
                @"\b(abstract|and|array|as|break|case|catch|cfunction|class|clone|const|continue|declare|default|do|else|elseif|enddeclare|endfor|endforeach|endif|endswitch|endwhile|extends|final|for|foreach|function|global|goto|if|implements|instanceof|interface|namespace|new|or|private|protected|public|static|switch|throw|try|use|var|while|xor)\b",
                RegexCompiledOption);
        PHPKeywordRegex3 = new Regex (@"__CLASS__|__DIR__|__FILE__|__LINE__|__FUNCTION__|__METHOD__|__NAMESPACE__",
            RegexCompiledOption);
    }

    /// <summary>
    /// Highlights PHP code
    /// </summary>
    /// <param name="range"></param>
    public virtual void PHPSyntaxHighlight (TextRange range)
    {
        range._textBox.CommentPrefix = "//";
        range._textBox.LeftBracket = '(';
        range._textBox.RightBracket = ')';
        range._textBox.LeftBracket2 = '{';
        range._textBox.RightBracket2 = '}';
        range._textBox.BracketsHighlightStrategy = BracketsHighlightStrategy.Strategy2;

        //clear style of changed range
        range.ClearStyle (StringStyle, CommentStyle, NumberStyle, VariableStyle, KeywordStyle, KeywordStyle2,
            KeywordStyle3);

        range._textBox.AutoIndentCharsPatterns
            = @"
^\s*\$[\w\.\[\]\'\""]+\s*(?<range>=)\s*(?<range>[^;]+);
";

        //
        if (PHPStringRegex == null)
        {
            InitPHPRegex();
        }

        //string highlighting
        range.SetStyle (StringStyle, PHPStringRegex);

        //comment highlighting
        range.SetStyle (CommentStyle, PHPCommentRegex1);
        range.SetStyle (CommentStyle, PHPCommentRegex2);
        range.SetStyle (CommentStyle, PHPCommentRegex3);

        //number highlighting
        range.SetStyle (NumberStyle, PHPNumberRegex);

        //var highlighting
        range.SetStyle (VariableStyle, PHPVarRegex);

        //keyword highlighting
        range.SetStyle (KeywordStyle, PHPKeywordRegex1);
        range.SetStyle (KeywordStyle2, PHPKeywordRegex2);
        range.SetStyle (KeywordStyle3, PHPKeywordRegex3);

        //clear folding markers
        range.ClearFoldingMarkers();

        //set folding markers
        range.SetFoldingMarkers ("{", "}"); //allow to collapse brackets block
        range.SetFoldingMarkers (@"/\*", @"\*/"); //allow to collapse comment block
    }

    /// <summary>
    ///
    /// </summary>
    protected void InitJScriptRegex()
    {
        JScriptStringRegex = new Regex (@"""""|''|"".*?[^\\]""|'.*?[^\\]'", RegexCompiledOption);
        JScriptCommentRegex1 = new Regex (@"//.*$", RegexOptions.Multiline | RegexCompiledOption);
        JScriptCommentRegex2 = new Regex (@"(/\*.*?\*/)|(/\*.*)", RegexOptions.Singleline | RegexCompiledOption);
        JScriptCommentRegex3 = new Regex (@"(/\*.*?\*/)|(.*\*/)",
            RegexOptions.Singleline | RegexOptions.RightToLeft | RegexCompiledOption);
        JScriptNumberRegex = new Regex (@"\b\d+[\.]?\d*([eE]\-?\d+)?[lLdDfF]?\b|\b0x[a-fA-F\d]+\b",
            RegexCompiledOption);
        JScriptKeywordRegex =
            new Regex (
                @"\b(true|false|break|case|catch|const|continue|default|delete|do|else|export|for|function|if|in|instanceof|new|null|return|switch|this|throw|try|var|void|while|with|typeof)\b",
                RegexCompiledOption);
    }

    /// <summary>
    /// Highlights JavaScript code
    /// </summary>
    /// <param name="range"></param>
    public virtual void JScriptSyntaxHighlight (TextRange range)
    {
        range._textBox.CommentPrefix = "//";
        range._textBox.LeftBracket = '(';
        range._textBox.RightBracket = ')';
        range._textBox.LeftBracket2 = '{';
        range._textBox.RightBracket2 = '}';
        range._textBox.BracketsHighlightStrategy = BracketsHighlightStrategy.Strategy2;

        range._textBox.AutoIndentCharsPatterns
            = @"
^\s*[\w\.]+(\s\w+)?\s*(?<range>=)\s*(?<range>[^;]+);
";

        //clear style of changed range
        range.ClearStyle (StringStyle, CommentStyle, NumberStyle, KeywordStyle);

        //
        if (JScriptStringRegex == null)
        {
            InitJScriptRegex();
        }

        //string highlighting
        range.SetStyle (StringStyle, JScriptStringRegex);

        //comment highlighting
        range.SetStyle (CommentStyle, JScriptCommentRegex1);
        range.SetStyle (CommentStyle, JScriptCommentRegex2);
        range.SetStyle (CommentStyle, JScriptCommentRegex3);

        //number highlighting
        range.SetStyle (NumberStyle, JScriptNumberRegex);

        //keyword highlighting
        range.SetStyle (KeywordStyle, JScriptKeywordRegex);

        //clear folding markers
        range.ClearFoldingMarkers();

        //set folding markers
        range.SetFoldingMarkers ("{", "}"); //allow to collapse brackets block
        range.SetFoldingMarkers (@"/\*", @"\*/"); //allow to collapse comment block
    }

    /// <summary>
    ///
    /// </summary>
    protected void InitLuaRegex()
    {
        LuaStringRegex = new Regex (@"""""|''|"".*?[^\\]""|'.*?[^\\]'", RegexCompiledOption);
        LuaCommentRegex1 = new Regex (@"--.*$", RegexOptions.Multiline | RegexCompiledOption);
        LuaCommentRegex2 = new Regex (@"(--\[\[.*?\]\])|(--\[\[.*)", RegexOptions.Singleline | RegexCompiledOption);
        LuaCommentRegex3 = new Regex (@"(--\[\[.*?\]\])|(.*\]\])",
            RegexOptions.Singleline | RegexOptions.RightToLeft | RegexCompiledOption);
        LuaNumberRegex = new Regex (@"\b\d+[\.]?\d*([eE]\-?\d+)?[lLdDfF]?\b|\b0x[a-fA-F\d]+\b",
            RegexCompiledOption);
        LuaKeywordRegex =
            new Regex (
                @"\b(and|break|do|else|elseif|end|false|for|function|if|in|local|nil|not|or|repeat|return|then|true|until|while)\b",
                RegexCompiledOption);

        LuaFunctionsRegex =
            new Regex (
                @"\b(assert|collectgarbage|dofile|error|getfenv|getmetatable|ipairs|load|loadfile|loadstring|module|next|pairs|pcall|print|rawequal|rawget|rawset|require|select|setfenv|setmetatable|tonumber|tostring|type|unpack|xpcall)\b",
                RegexCompiledOption);
    }

    /// <summary>
    /// Highlights Lua code
    /// </summary>
    /// <param name="range"></param>
    public virtual void LuaSyntaxHighlight (TextRange range)
    {
        range._textBox.CommentPrefix = "--";
        range._textBox.LeftBracket = '(';
        range._textBox.RightBracket = ')';
        range._textBox.LeftBracket2 = '{';
        range._textBox.RightBracket2 = '}';
        range._textBox.BracketsHighlightStrategy = BracketsHighlightStrategy.Strategy2;

        range._textBox.AutoIndentCharsPatterns
            = @"
^\s*[\w\.]+(\s\w+)?\s*(?<range>=)\s*(?<range>.+)
";

        //clear style of changed range
        range.ClearStyle (StringStyle, CommentStyle, NumberStyle, KeywordStyle, FunctionsStyle);

        //
        if (LuaStringRegex == null)
        {
            InitLuaRegex();
        }

        //string highlighting
        range.SetStyle (StringStyle, LuaStringRegex);

        //comment highlighting
        range.SetStyle (CommentStyle, LuaCommentRegex1);
        range.SetStyle (CommentStyle, LuaCommentRegex2);
        range.SetStyle (CommentStyle, LuaCommentRegex3);

        //number highlighting
        range.SetStyle (NumberStyle, LuaNumberRegex);

        //keyword highlighting
        range.SetStyle (KeywordStyle, LuaKeywordRegex);

        //functions highlighting
        range.SetStyle (FunctionsStyle, LuaFunctionsRegex);

        //clear folding markers
        range.ClearFoldingMarkers();

        //set folding markers
        range.SetFoldingMarkers ("{", "}"); //allow to collapse brackets block
        range.SetFoldingMarkers (@"--\[\[", @"\]\]"); //allow to collapse comment block
    }

    /// <summary>
    ///
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="args"></param>
    protected void LuaAutoIndentNeeded
        (
            object? sender,
            AutoIndentEventArgs args
        )
    {
        //end of block
        if (Regex.IsMatch (args.LineText, @"^\s*(end|until)\b"))
        {
            args.Shift = -args.TabLength;
            args.ShiftNextLines = -args.TabLength;
            return;
        }

        // then ...
        if (Regex.IsMatch (args.LineText, @"\b(then)\s*\S+"))
        {
            return;
        }

        //start of operator block
        if (Regex.IsMatch (args.LineText, @"^\s*(function|do|for|while|repeat|if)\b"))
        {
            args.ShiftNextLines = args.TabLength;
            return;
        }

        //Statements else, elseif, case etc
        if (Regex.IsMatch (args.LineText, @"^\s*(else|elseif)\b", RegexOptions.IgnoreCase))
        {
            args.Shift = -args.TabLength;
            return;
        }
    }

    protected void InitJSONRegex()
    {
        JSONStringRegex = new Regex (@"""([^\\""]|\\"")*""", RegexCompiledOption);
        JSONNumberRegex = new Regex (@"\b(\d+[\.]?\d*|true|false|null)\b", RegexCompiledOption);
        JSONKeywordRegex = new Regex (@"(?<range>""([^\\""]|\\"")*"")\s*:", RegexCompiledOption);
    }

    /// <summary>
    /// Highlights JSON code
    /// </summary>
    /// <param name="range"></param>
    public virtual void JSONSyntaxHighlight (TextRange range)
    {
        range._textBox.LeftBracket = '[';
        range._textBox.RightBracket = ']';
        range._textBox.LeftBracket2 = '{';
        range._textBox.RightBracket2 = '}';
        range._textBox.BracketsHighlightStrategy = BracketsHighlightStrategy.Strategy2;

        range._textBox.AutoIndentCharsPatterns
            = @"
^\s*[\w\.]+(\s\w+)?\s*(?<range>=)\s*(?<range>[^;]+);
";

        //clear style of changed range
        range.ClearStyle (StringStyle, NumberStyle, KeywordStyle);

        //
        if (JSONStringRegex == null)
        {
            InitJSONRegex();
        }

        //keyword highlighting
        range.SetStyle (KeywordStyle, JSONKeywordRegex);

        //string highlighting
        range.SetStyle (StringStyle, JSONStringRegex);

        //number highlighting
        range.SetStyle (NumberStyle, JSONNumberRegex);

        //clear folding markers
        range.ClearFoldingMarkers();

        //set folding markers
        range.SetFoldingMarkers ("{", "}"); //allow to collapse brackets block
        range.SetFoldingMarkers (@"\[", @"\]"); //allow to collapse comment block
    }

    #region Styles

    /// <summary>
    /// String style
    /// </summary>
    public Style StringStyle { get; set; }

    /// <summary>
    /// Comment style
    /// </summary>
    public Style CommentStyle { get; set; }

    /// <summary>
    /// Number style
    /// </summary>
    public Style NumberStyle { get; set; }

    /// <summary>
    /// C# attribute style
    /// </summary>
    public Style AttributeStyle { get; set; }

    /// <summary>
    /// Class name style
    /// </summary>
    public Style ClassNameStyle { get; set; }

    /// <summary>
    /// Keyword style
    /// </summary>
    public Style KeywordStyle { get; set; }

    /// <summary>
    /// Style of tags in comments of C#
    /// </summary>
    public Style CommentTagStyle { get; set; }

    /// <summary>
    /// HTML attribute value style
    /// </summary>
    public Style AttributeValueStyle { get; set; }

    /// <summary>
    /// HTML tag brackets style
    /// </summary>
    public Style TagBracketStyle { get; set; }

    /// <summary>
    /// HTML tag name style
    /// </summary>
    public Style TagNameStyle { get; set; }

    /// <summary>
    /// HTML Entity style
    /// </summary>
    public Style HtmlEntityStyle { get; set; }

    /// <summary>
    /// XML attribute style
    /// </summary>
    public Style XmlAttributeStyle { get; set; }

    /// <summary>
    /// XML attribute value style
    /// </summary>
    public Style XmlAttributeValueStyle { get; set; }

    /// <summary>
    /// XML tag brackets style
    /// </summary>
    public Style XmlTagBracketStyle { get; set; }

    /// <summary>
    /// XML tag name style
    /// </summary>
    public Style XmlTagNameStyle { get; set; }

    /// <summary>
    /// XML Entity style
    /// </summary>
    public Style XmlEntityStyle { get; set; }

    /// <summary>
    /// XML CData style
    /// </summary>
    public Style XmlCDataStyle { get; set; }

    /// <summary>
    /// Variable style
    /// </summary>
    public Style VariableStyle { get; set; }

    /// <summary>
    /// Specific PHP keyword style
    /// </summary>
    public Style KeywordStyle2 { get; set; }

    /// <summary>
    /// Specific PHP keyword style
    /// </summary>
    public Style KeywordStyle3 { get; set; }

    /// <summary>
    /// SQL Statements style
    /// </summary>
    public Style StatementsStyle { get; set; }

    /// <summary>
    /// SQL Functions style
    /// </summary>
    public Style FunctionsStyle { get; set; }

    /// <summary>
    /// SQL Types style
    /// </summary>
    public Style TypesStyle { get; set; }

    #endregion
}

/// <summary>
/// Language
/// </summary>
public enum Language
{
    /// <summary>
    ///
    /// </summary>
    Custom,

    /// <summary>
    ///
    /// </summary>
    CSharp,

    /// <summary>
    ///
    /// </summary>
    VB,

    /// <summary>
    ///
    /// </summary>
    HTML,

    /// <summary>
    ///
    /// </summary>
    XML,

    /// <summary>
    ///
    /// </summary>
    SQL,

    /// <summary>
    ///
    /// </summary>
    PHP,

    /// <summary>
    ///
    /// </summary>
    JS,

    /// <summary>
    ///
    /// </summary>
    Lua,

    /// <summary>
    ///
    /// </summary>
    JSON
}
